﻿
namespace Patterns4Net.PatternEnforcer.Core.Patterns
{
    using System;
    using System.ComponentModel.Composition;
    using System.Diagnostics.Contracts;
    using System.Linq;
    using Patterns4Net.Attributes;

    [Export(typeof(IPatternCheckersLocator))]
    public class MEFPatternCheckersLocator : IPatternCheckersLocator
    {
        private readonly Lazy<IPatternChecker, PatternCheckerExportAttribute>[] checkers;

        [ImportingConstructor]
        public MEFPatternCheckersLocator(
            [ImportMany]Lazy<IPatternChecker, PatternCheckerExportAttribute>[] checkers)
        {
            Contract.Requires(checkers != null);
            this.checkers = checkers;
        }

        public IPatternChecker GetCheckerFor(IPattern patternBase)
        {
            return ResolveChecker(this.checkers, patternBase);
        }

        private static IPatternChecker ResolveChecker(
            Lazy<IPatternChecker, PatternCheckerExportAttribute>[] checkers,
            IPattern patternBase)
        {
            var result = checkers.Where(x => x.Metadata.PatternType == patternBase.GetType());

            if (result.Count() == 0)
            {
                return null;
            }

            if (result.Count() == 1)
            {
                return result.First().Value;
            }

            if (result.Count() == 2 && result.Any(x => x.Metadata.OverridesDefault))
            {
                return result.First(x => x.Metadata.OverridesDefault).Value;
            }

            if (result.Count() > 2)
            {
                var message = string.Format(
                    @"There are more than 2 checkers for the pattern {0}. {1} cannot decide which one to choose.",
                    patternBase.GetType().Name,
                    typeof(MEFPatternCheckersLocator).GetType().Name);
                throw new CheckerLocationException(message);
            }

            const string Message = @"There are two checkers for the pattern {0}, 
                    but no one has OverridesDefault=true in PatternCheckerExportAttribute.
                    If you want to use your custom checker for {0}, you have to set OverridesDefault=true.
                    (Exception origin: class {1})";
            throw new CheckerLocationException(string.Format(Message, patternBase.GetType().Name, typeof(MEFPatternCheckersLocator).Name));
        }

        public class CheckerLocationException : ApplicationException
        {
            public CheckerLocationException(string message)
                : base(message)
            {
                Contract.Requires(message != null);
            }
        }
    }
}
